#include "stdafx.h"
#include "ProjectItemProperties.h"
#include "Project.h"
#include <LitSceneManager.h>
#include <LitCore.h>

ProjectItemPreview::ProjectItemPreview( QWidget*parent ) 
	: OgreSubWidget(parent)
{
	setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding );
	setMinimumSize( 128, 128 );

	connect( (OgreSubWidget*)this, SIGNAL(ogreInitialise()), this, SLOT(ogreInitialise()) );
	connect( (OgreSubWidget*)this, SIGNAL(ogreShutdown()), this, SLOT(ogreShutdown()) );
}
ProjectItemPreview::~ProjectItemPreview()
{
}

void ProjectItemPreview::replaceMesh( const std::string &mesh ){
	std::string curMesh = mEntity->getMeshName();
	if( !mEntity->replaceMesh( mesh ) )
	{
		mEntity->replaceMesh( curMesh );
	}
	fitEntity();
	
}

void ProjectItemPreview::fitEntity() 
{
	if( !mEntity->isCreated() )
		return;
	Ogre::AxisAlignedBox bbox = mEntity->boundingBox();

	Ogre::Vector3 offset = bbox.getMaximum() - bbox.getCenter(); // Radius of bounding sphere
	Ogre::Vector3 center = bbox.getCenter();

	mCamera->setPosition( center );
	float oneOverSine = 1.0f / (float)Ogre::Math::Tan( mCamera->getFOVy().valueRadians()/2.0 );
	float distanceToCenter = offset.length() * oneOverSine; // (adjacent / opposite) * opposite = adjacent
	mCamera->move( Ogre::Vector3(1, 1, 1).normalisedCopy() * distanceToCenter);
	mCamera->lookAt( center );
}

Lit::EntityPtr& ProjectItemPreview::getEntity(){ return mEntity; }
const Lit::EntityPtr& ProjectItemPreview::getEntity() const{ return mEntity; }

Lit::LightPtr& ProjectItemPreview::getLight(){ return mLight; }
const Lit::LightPtr& ProjectItemPreview::getLight() const{ return mLight; }

bool __wasvisible=false;
void ProjectItemPreview::preViewportUpdate(const Ogre::RenderTargetViewportEvent& evt)
{ __wasvisible = Lit::Root::PhysicsManager()->getWorld()->getDebugger()->isVisible(); Lit::Root::PhysicsManager()->getWorld()->getDebugger()->setVisible( false ); }
void ProjectItemPreview::postViewportUpdate(const Ogre::RenderTargetViewportEvent& evt)
{ Lit::Root::PhysicsManager()->getWorld()->getDebugger()->setVisible( __wasvisible ); }

void ProjectItemPreview::ogreInitialise()
{
	mSceneNode = mSceneManager->getRootSceneNode()->createChildSceneNode();
	mEntity = new Lit::Entity( "Preview Entity", "penguin.mesh", *mSceneNode );
	mEntity->create( mSceneManager );
	mLight = new Lit::Light( "Preview Light", Lit::LT_DIRECTIONAL, Ogre::Vector3::ZERO, Ogre::Vector3::NEGATIVE_UNIT_Y );
	mLight->create( mSceneManager );
	mRenderWindow->addListener( this );
	mRoot->addFrameListener( this );
}
void ProjectItemPreview::ogreShutdown()
{
	mRoot->removeFrameListener( this );
	mEntity.destroy();
	mLight.destroy();
	mSceneNode->detachAllObjects();
	mSceneManager->destroySceneNode( *mSceneNode );
}
bool ProjectItemPreview::frameRenderingQueued( const Ogre::FrameEvent& evt )
{
	if( mEntity )
	{
		mEntity->getNode()->pitch( Ogre::Degree(mDrag.Delta.y())*evt.timeSinceLastFrame );
		mEntity->getNode()->yaw( Ogre::Degree(mDrag.Delta.x())*evt.timeSinceLastFrame );
	}
	return true;
}

ProjectItemProperties::ProjectItemProperties( QWidget*parent ) 
	: SettingsWidget(parent)
	, mPreview(NULL)
	, mProject( NULL )
{
	connect( 
		(SettingsWidget*)this, 
		SIGNAL(settingChanged( const std::string &, const std::string &, const std::string & )), 
		this, 
		SLOT(propertyChanged( const std::string &, const std::string &, const std::string & ))
	);
	connect( 
		(SettingsWidget*)this, 
		SIGNAL(currentPageChanged( int )), 
		this, 
		SLOT(pageUpdateRequested( int ))
	);
	setContentsMargins(0,0,0,0);
	int mw = 0; 
}
ProjectItemProperties::~ProjectItemProperties(){
	clearPages();
}

void ProjectItemProperties::setProject( Project* proj )
{
	mProject = proj;
}

void ProjectItemProperties::pageUpdateRequested( int id ){	reloadPage( findPageName( id ) ); }

void ProjectItemProperties::setPreview( ProjectItemPreview * prev ){ mPreview = prev; }
RenderViewport* ProjectItemProperties::viewport(){ return mViewport; }
void ProjectItemProperties::setViewport( RenderViewport * vp ){mViewport = vp;}
ProjectItemPreview* ProjectItemProperties::preview(){ return mPreview;}
void ProjectItemProperties::subEntitySettingChanged(const std::string& name, const std::string& value){
	if( !mCurrentNode ) return;
	if( name == "Material" )
	{
		if( mCurrentNode->movable()->getMovableType() == Lit::MT_ENTITY )
			((Lit::Entity*)*mCurrentNode->movable())->setMaterialName( value );
	}
	else if( name == "Visible" )
	{
		mCurrentNode->movable()->getNode()->setVisible(Ogre::StringConverter::parseBool(value));
	}
}

void ProjectItemProperties::propertyChanged( const std::string &page, const std::string &name, const std::string &value )
{
	if( !mCurrentNode ) return;
	LIT_LOG("Applying property "+mCurrentNode->getName()+":"+name);
	if( mCurrentNode && mCurrentNode->movable() )
	{
		if( name == "Position" ) mCurrentNode->movable()->setPosition( Ogre::StringConverter::parseVector3(value) );
		if( name == "Orientation" ) mCurrentNode->movable()->setOrientation( Ogre::StringConverter::parseQuaternion(value) );
	}
	if( name == "Sub Entities" )
	{
		size_t id = Lit::String(value).to<size_t>();
		Lit::EntityPtr ent(((Lit::Entity*)*mCurrentNode->movable()));
		if( ent && id < ent->getEntity()->getNumSubEntities() )
		{
			Ogre::SubEntity* sub = ent->getEntity()->getSubEntity(id);
			SettingsTable * table = NULL;
			if( !(*findPage(page))->table->hasSetting("SubProperties") )
			{
				table = (*findPage(page))->table->addSettingsTable( "SubProperties" )->table();

				Lit::StringList mats(sub->getMaterialName());
				for( auto i=mProject->resources()->materials().begin(); 
					i!=mProject->resources()->materials().end(); 
					i++ 
				) mats((*i).getName());
				table->addSetting("Material",mats, true);
				table->addSetting("Visible", sub->isVisible(), true);

				connect( table, SIGNAL(settingChanged(const std::string&, const std::string&)), this, SLOT(subEntitySettingChanged(const std::string&, const std::string&)));
			}
			else 
			{
				table = (*findPage(page))->table->getSetting<SettingsTableItem>("SubProperties")->table();
				SettingsComboItem * mats = table->getSetting<SettingsComboItem>("Material");
				SettingsCheckboxItem * vis = table->getSetting<SettingsCheckboxItem>("Visible");

				mats->setValue(Lit::StringList(sub->getMaterialName(), mats->listValue()));
				vis->setValue( sub->isVisible() );
			}
		}
	}
	savePage( page );
}

void ProjectItemProperties::updateProperties( ProjectNodePtr&node )
{
	if( !node ) return;
	if( mCurrentNode == node ) return;
	mCurrentNode = node;

	clearPages();
	if( node->movable() )
	{
		Lit::MovablePtr mov(node->movable());
		updatePageProperties( createPage("General"), mov );
	}
	else
	{
		Page * p = createPage("General");
		p->table->addSetting( "Name", node->getName(), false );
		p->table->addSetting( "Type", node->typeStr(), false );
	}
}
void ProjectItemProperties::updateSelectionProperties( Lit::SceneSelectionPtr& sel ){
	if( !mProject ) return;
	clearPages();
	for( auto i=sel->begin(); i!=sel->end(); ++i )
	{
		Lit::MovablePtr mov(*i);
		if( mov ){
			updatePageProperties( hasPage((*i)->getName())?*findPage((*i)->getName()):createPage((*i)->getName()), mov );
		}
	}
}
template<typename T, typename ValType>
T* createOrRetrieveSetting( SettingsTable* table, const std::string &name, bool modifiable = true, ValType default_val = ValType() )
{
	T * ret = NULL;
	if( table->hasSetting( name ) ){
		ret = table->getSetting<T>( name );
	}
	else ret = table->addSetting( name, default_val, false );
	ret->setEnabled( modifiable );
	return ret;
}
template<typename T, typename ValType>
T* updateSetting( SettingsTable* table, const std::string &name, ValType default_val = ValType(), bool modifiable = true )
{
	T * ret = createOrRetrieveSetting<T, ValType>(table, name, modifiable, default_val);
	ret->setValue(default_val);
	return ret;
}
SettingsWidget::Page * ProjectItemProperties::updatePageProperties( SettingsWidget::Page * page, Lit::MovablePtr mov )
{
	if( !page || !mov ) return page;
	if( mCurrentNode == mProject->findNode(mov) ) return page;
	mCurrentNode = mProject->findNode(mov);
	LIT_LOG("Updating "+mov->getName());	
	Lit::StringList movList = Lit::StringList(mov->getMovableTypeStr())
		(Lit::trMovableType(Lit::MT_ENTITY))
		(Lit::trMovableType(Lit::MT_PLANE))
		(Lit::trMovableType(Lit::MT_LIGHT))
		(Lit::trMovableType(Lit::MT_SOUND));
	
	updateSetting<SettingsTextItem>( page->table, "Name", mov->getName(), false );
	updateSetting<SettingsComboItem>( page->table, "Type", movList, false );
	updateSetting<SettingsVectorItem>( page->table, "Position", mov->getPosition() );
	updateSetting<SettingsQuaternionItem>( page->table, "Orientation", mov->getOrientation() );

	try
	{
		switch( mov->getMovableType() )
		{
			case Lit::MT_ENTITY:
			{
				Lit::EntityPtr ent = (Lit::Entity*)(*mov);
				updateSetting<SettingsTextItem>( page->table, "Mesh", ent->getMeshName() );

				Lit::StringList lst(0);
				for( size_t i=0; i<ent->getEntity()->getNumSubEntities(); i++ ) lst(Lit::String(i).str());

				SettingsComboItem * cb = updateSetting<SettingsComboItem>( page->table, "Sub Entities", lst );
				cb->emitItemChanged();
				break;
			}
			case Lit::MT_LIGHT:
			{
				try
				{
					Lit::LightPtr light = (Lit::Light*)(*mov);
					Lit::StringList lightList = Lit::StringList(light->typestr())
						(Lit::trLightType(Lit::LT_POINT))
						(Lit::trLightType(Lit::LT_DIRECTIONAL))
						(Lit::trLightType(Lit::LT_SPOTLIGHT));
					
					updateSetting<SettingsComboItem>( page->table, "Type", lightList );
					updateSetting<SettingsVectorItem>( page->table, "Direction", light->direction() );
					updateSetting<SettingsColorItem>( page->table, "Diffuse Color", light->diffuseColor() );
					updateSetting<SettingsColorItem>( page->table, "Specular Color", light->specularColor() );
					updateSetting<SettingsTextItem>( page->table, "Attenuation Range", Ogre::StringConverter::toString(light->attenuationRange()) );
					updateSetting<SettingsTextItem>( page->table, "Attenuation Constant", Ogre::StringConverter::toString(light->attenuationConstant()) );
					updateSetting<SettingsTextItem>( page->table, "Attenuation Linear", Ogre::StringConverter::toString(light->attenuationLinear()) );
					updateSetting<SettingsTextItem>( page->table, "Attenuation Quadratic", Ogre::StringConverter::toString(light->attenuationQuadratic()) );

					if( light->type() == Lit::LT_SPOTLIGHT )
					{
						updateSetting<SettingsDegreeItem>( page->table, "Spotlight Inner Angle", light->spotlightInnerAngle() );
						updateSetting<SettingsDegreeItem>( page->table, "Spotlight Outer Angle", light->spotlightOuterAngle() );
						updateSetting<SettingsDegreeItem>( page->table, "Spotlight Falloff", Ogre::Degree(light->spotlightFalloff()) );
					}
				}
				catch( ... )
				{}
				break;
			}
			case Lit::MT_SOUND:
			{
				break;
			}
		};
	}
	catch( ... )
	{ throw; }
	adjustSize();
	return page;
}